package jsr292.java.lang.invoke;

import jsr292.weaver.Capabilities;
import jsr292.weaver.Debug;
import jsr292.weaver.opt.OptimizerContext;
import jsr292.weaver.opt.OptimizerSnippets;
import jsr292.weaver.opt.OptimizerVarAllocator;

class MHExactInvoker extends MethodHandle {
    MethodHandle mhCache;
    
    public MHExactInvoker(MethodType methodType) {
        super(methodType);
    }
    
    @Override
    public Object $invoke$(Object o1) throws Throwable {
        return (mhCache=(MethodHandle)o1).$invoke$();
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2) throws Throwable {
        return (mhCache=(MethodHandle)o1).$invoke$(o2);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3) throws Throwable {
        return (mhCache=(MethodHandle)o1).$invoke$(o2, o3);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4) throws Throwable {
        return (mhCache=(MethodHandle)o1).$invoke$(o2, o3, o4);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4, Object o5) throws Throwable {
        return (mhCache=(MethodHandle)o1).$invoke$(o2, o3, o4, o5);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6) throws Throwable {
        return (mhCache=(MethodHandle)o1).$invoke$(o2, o3, o4, o5, o6);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7) throws Throwable {
        return (mhCache=(MethodHandle)o1).$invoke$(o2, o3, o4, o5, o6, o7);
    }
    
    @Override
    public Object $invoke$(Object... rest) throws Throwable {
        if (rest.length == 8) {
            return (mhCache=(MethodHandle)rest[0]).$invoke$(rest[1], rest[2], rest[3], rest[4], rest[5], rest[6], rest[7]);
        }
        return (mhCache=(MethodHandle)rest[0]).$invoke$(ArrayUtils.copyOfRange(rest, 1, rest.length - 1));
    }
    
    @Override
    public boolean isWeavable(Class<?> classBeingRedefined) {
        if (!Capabilities.canDefine()) {
            if (Debug.DEBUG) System.err.println("invoke exact requires define capability");
            return false;
        }
        
        // works even if mhCache is null or not weavable
        return true;
    }
    @Override
    public void weave(OptimizerContext context, OptimizerVarAllocator allocator) {
        if (Debug.DEBUG) {
            Debug.checkAllocator(allocator, this);
        }
        
	OptimizerSnippets.exactInvoker(context, allocator, type(), mhCache);
    }
}
